package iso3.pt.dao;

import iso3.pt.model.Alumno;
import iso3.pt.model.Asignatura;
import iso3.pt.model.Evaluacion;
import iso3.pt.model.Profesor;
import iso3.pt.model.Unidad;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class PtDAO implements IPtDAO {
	SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
	Session session = sessionFactory.openSession();
	private HashMap<Integer, Asignatura> cacheAsignaturas = null;
	
	private static PtDAO ptdao;
	
	private PtDAO(){
		this.cacheAsignaturas();
	}
	
	public static synchronized PtDAO getInstance() {
		if (ptdao == null)
			ptdao = new PtDAO();
		return ptdao;
	}
	
	public Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}
	
	private void cacheAsignaturas() {
		cacheAsignaturas = new HashMap<Integer, Asignatura>();
		Set<Asignatura> asignaturas = this.getAsignaturas();
		
		for(Asignatura asignatura: asignaturas)
			cacheAsignaturas.put(asignatura.getId(), asignatura);
	}
	
	@Override
	public Profesor getProfesor(int idAsignatura) {
		Asignatura asignatura = this.getAsignatura(idAsignatura);
        if(asignatura != null)
        	return asignatura.getProfesor();
        return null;
	}

	@Override
	public Set<Alumno> getAlumnos(int idAsignatura) {
		Asignatura asignatura = this.getAsignatura(idAsignatura);
		if (asignatura != null)
			return asignatura.getListaAlumnos();
		return null;
	}

	@Override
	public List<Evaluacion> getEvaluacionesOrderedByAsignatura(int idAlumno) {
		Alumno alumno = null;
		alumno = this.getAlumno(idAlumno);
		List<Evaluacion> result = new ArrayList<Evaluacion>();
		
		if (alumno != null) {
			Set<Evaluacion> evaluaciones = alumno.getListaEvaluaciones();
			for(Iterator<Evaluacion> it = evaluaciones.iterator(); it.hasNext();)
				result.add(it.next());
		}
		
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set<Evaluacion> getEvaluaciones(int idAsignatura, int idAlumno) {
		List<Evaluacion> evals = session.createQuery("FROM Evaluacion AS eval WHERE eval.asignatura.id = " + idAsignatura + "AND eval.alumno.id = " + idAlumno).list();
		Set<Evaluacion> ret = new HashSet<Evaluacion>();
		for (Evaluacion e : evals)
			ret.add(e);
		return ret;
	}

	@Override
	public void addEvaluacion(String concepto, float nota, int idAsignatura, int idAlumno) {
		Transaction tx = session.beginTransaction();
		Asignatura asignatura = this.getAsignatura(idAsignatura);
		Alumno alumno  = null;
		alumno = this.getAlumno(idAlumno);

		Evaluacion evaluacion = new Evaluacion(concepto, nota);
		evaluacion.setAlumno(alumno);
		evaluacion.setAsignatura(asignatura);

		session.save(asignatura);
		session.save(evaluacion);
		session.save(alumno);
		tx.commit();
	}

	@Override
	public Set<Unidad> getUnidades(int idAsignatura) {
		Asignatura asignatura = this.getAsignatura(idAsignatura);
		if(asignatura != null)
			return asignatura.getListaUnidades();
		return null;
	}

	@Override
	public Set<Asignatura> getAsignaturas() {
		List<Asignatura> asignaturas = createList(Asignatura.class, session.createQuery("FROM Asignatura").list());
		
		Set<Asignatura> result = new HashSet<Asignatura>();
		for (Asignatura asignatura: asignaturas)
			result.add(asignatura);		
		return result;
	}

	@Override
	public Alumno getAlumno(int dni) {
		return (Alumno) session.get(Alumno.class, dni);
	}

	@Override
	public Asignatura getAsignatura(int id) {
		Asignatura asignatura = cacheAsignaturas.get(id);
		if(asignatura == null)
			asignatura = (Asignatura) session.get(Asignatura.class, id);
		return asignatura;
	}

	@Override
	public Alumno loginAlumno(int dni, String pass) throws UserNotFoundException, IncorrectPasswordException {
		Alumno alumno = this.getAlumno(dni);
		
		if(!alumno.getPassword().equals(pass))
			throw new IncorrectPasswordException();
		return alumno;
	}

	@Override
	public Set<Asignatura> getAsignaturas(int idAlumno) {
		Alumno alumno = null;
		alumno = this.getAlumno(idAlumno);
		if (alumno != null)
			return alumno.getListaAsignaturas();
		return null;
	}

	@Override
	public void matricular(int idAlumno, int idAsignatura) {
		Transaction tx = session.beginTransaction();
		Alumno alumno = this.getAlumno(idAlumno);

		Asignatura asignatura = this.getAsignatura(idAsignatura);
		
		alumno.addAsignatura(asignatura);
		asignatura.addAlumno(alumno);
		
		session.save(alumno);
		session.save(asignatura);
		tx.commit();
	}

	@Override
	public void desmatricular(int idAlumno, int idAsignatura) {
		Transaction tx = session.beginTransaction();
		Alumno alumno  = null;
		alumno = this.getAlumno(idAlumno);

		Asignatura asignatura = this.getAsignatura(idAsignatura);
		
		asignatura.removeAlumno(alumno);
		alumno.removeAsignatura(asignatura);
		
		session.save(alumno);
		session.save(asignatura);
		tx.commit();
	}

	@Override
	public Profesor loginProfesor(int dni, String pass) throws UserNotFoundException, IncorrectPasswordException {
		Profesor profesor = this.getProfesorByDni(dni);
		if (!profesor.getPassword().equals(pass))
			throw new IncorrectPasswordException();
		return profesor;
	}

	@Override
	public Set<Asignatura> getAsignaturasProfesor(int idProfesor) {
		Set<Asignatura> asignaturas = new HashSet<Asignatura>();
		
		for (Asignatura asignatura: this.getAsignaturas())
			if (asignatura.getProfesor() != null && asignatura.getProfesor().getId() == idProfesor)
				asignaturas.add(asignatura);
		
		return asignaturas;
	}

	@Override
	public Profesor getProfesorByDni(int dni) throws UserNotFoundException {
		List<Profesor> result = createList(Profesor.class, session.createQuery("FROM Profesor AS profe WHERE profe.dni = " + dni).list());
		if (result.get(0) == null)
			throw new UserNotFoundException();
		return result.get(0);
	}

	@Override
	public List<Evaluacion> getEvaluacionesAsignatura(int idAsignatura) {
		return createList(Evaluacion.class, session.createQuery("FROM Evaluacion AS eval WHERE eval.asignatura.id = " + idAsignatura).list());
	}
	
	public static <T> List<T> createList(Class<? extends T> clase, Collection<?> c) {
	    List<T> r = new ArrayList<T>(c.size());
	  
	    for(Object o: c)
	      r.add(clase.cast(o));
	    return r;
	}

}
